/* Serpent Cipher
Copyright (C) 2002 Dag Arne Osvik.
Copyright (C) 2003 Herbert Valerio Riedel.
Copyright (C) 2004 Jesús García Hernández.
Copyright (C) 2011-2018 Manuel Sainz de Baranda y Goñi.
Released under the terms of the GNU Lesser General Public License v3. */

#if defined(CIPHER_SERPENT_HIDE_API)
#	define CIPHER_SERPENT_API static
#elif defined(CIPHER_SERPENT_STATIC)
#	define CIPHER_SERPENT_API
#else
#	define CIPHER_SERPENT_API Z_API_EXPORT
#endif

#if defined(CIPHER_SERPENT_HIDE_ABI)
#	define CIPHER_SERPENT_ABI static
#elif defined(CIPHER_SERPENT_STATIC)
#	define CIPHER_SERPENT_ABI
#else
#	define CIPHER_SERPENT_ABI Z_API_EXPORT
#endif

#define CIPHER_SERPENT_OMIT_FUNCTION_PROTOTYPES

#if defined(CIPHER_SERPENT_USE_LOCAL_HEADER)
#	include "Serpent.h"
#else
#	include <cipher/Serpent.h>
#endif

#include <Z/functions/base/value.h>

#define PHI Z_UINT32(0x9E3779B9)
#define ROL z_uint32_rotate_left
#define ROR z_uint32_rotate_right

#define KI(a, b, c, d, i, j) \
	b ^= d; b ^= c; b ^= a; b ^= PHI ^ i; b = ROL(b, 11); k[j] = b;

#define READ_KEYS(x0, x1, x2, x3, i) \
	x0 = k[i]; x1 = k[i + 1]; x2 = k[i + 2]; x3 = k[i + 3];

#define WRITE_KEYS(x0, x1, x2, x3, i) \
	k[i] = x0; k[i + 1] = x1; k[i + 2] = x2; k[i + 3] = x3;

#define WRITE_AND_READ_KEYS(x0, x1, x2, x3, s, l) \
	WRITE_KEYS(x0, x1, x2, x3, s); READ_KEYS(x0, x1, x2, x3, l);

#define K(x0, x1, x2, x3, i) \
	x3 ^= k[4 * (i) + 3]; x2 ^= k[4 * (i) + 2]; \
	x1 ^= k[4 * (i) + 1]; x0 ^= k[4 * (i) + 0];

#define LK(x0, x1, x2, x3, x4, i)				    \
						x0  = ROL(x0, 13);  \
	x2  = ROL(x2, 3);   x1 ^= x0;		x4  = x0 << 3;	    \
	x3 ^= x2;	    x1 ^= x2;				    \
	x1  = ROL(x1, 1);   x3 ^= x4;				    \
	x3  = ROL(x3, 7);   x4	= x1;				    \
	x0 ^= x1;	    x4 <<= 7;		x2 ^= x3;	    \
	x0 ^= x3;	    x2 ^= x4;		x3 ^= k[4 * i + 3]; \
	x1 ^= k[4 * i + 1]; x0	= ROL(x0, 5);	x2  = ROL(x2, 22);  \
	x0 ^= k[4 * i + 0]; x2 ^= k[4 * i + 2];

#define KL(x0, x1, x2, x3, x4, i)				    \
	x0 ^= k[4 * i + 0]; x1 ^= k[4 * i + 1];	x2 ^= k[4 * i + 2]; \
	x3 ^= k[4 * i + 3]; x0	= ROR(x0, 5);	x2  = ROR(x2, 22);  \
	x4 =  x1;	    x2 ^= x3;		x0 ^= x3;	    \
	x4 <<= 7;	    x0 ^= x1;		x1  = ROR(x1, 1);   \
	x2 ^= x4;	    x3	= ROR(x3, 7);	x4  = x0 << 3;	    \
	x1 ^= x0;	    x3 ^= x4;		x0  = ROR(x0, 13);  \
	x1 ^= x2;	    x3 ^= x2;		x2  = ROR(x2, 3);

#define S0(x0, x1, x2, x3, x4)	      \
			    x4	= x3; \
	x3 |= x0; x0 ^= x4; x4 ^= x2; \
	x4 = ~x4; x3 ^= x1; x1 &= x0; \
	x1 ^= x4; x2 ^= x0; x0 ^= x3; \
	x4 |= x0; x0 ^= x2; x2 &= x1; \
	x3 ^= x2; x1 = ~x1; x2 ^= x4; \
	x1 ^= x2;

#define S1(x0, x1, x2, x3, x4)	      \
			    x4	= x1; \
	x1 ^= x0; x0 ^= x3; x3 = ~x3; \
	x4 &= x1; x0 |= x1; x3 ^= x2; \
	x0 ^= x3; x1 ^= x3; x3 ^= x4; \
	x1 |= x4; x4 ^= x2; x2 &= x0; \
	x2 ^= x1; x1 |= x0; x0 = ~x0; \
	x0 ^= x2; x4 ^= x1;

#define S2(x0, x1, x2, x3, x4)	      \
			    x3 = ~x3; \
	x1 ^= x0; x4  = x0; x0 &= x2; \
	x0 ^= x3; x3 |= x4; x2 ^= x1; \
	x3 ^= x1; x1 &= x0; x0 ^= x2; \
	x2 &= x3; x3 |= x1; x0 = ~x0; \
	x3 ^= x0; x4 ^= x0; x0 ^= x2; \
	x1 |= x2;

#define S3(x0, x1, x2, x3, x4)	      \
			    x4  = x1; \
	x1 ^= x3; x3 |= x0; x4 &= x0; \
	x0 ^= x2; x2 ^= x1; x1 &= x3; \
	x2 ^= x3; x0 |= x4; x4 ^= x3; \
	x1 ^= x0; x0 &= x3; x3 &= x4; \
	x3 ^= x2; x4 |= x1; x2 &= x1; \
	x4 ^= x3; x0 ^= x3; x3 ^= x2;

#define S4(x0, x1, x2, x3, x4)	      \
			    x4	= x3; \
	x3 &= x0; x0 ^= x4;	      \
	x3 ^= x2; x2 |= x4; x0 ^= x1; \
	x4 ^= x3; x2 |= x0;	      \
	x2 ^= x1; x1 &= x0;	      \
	x1 ^= x4; x4 &= x2; x2 ^= x3; \
	x4 ^= x0; x3 |= x1; x1 = ~x1; \
	x3 ^= x0;

#define S5(x0, x1, x2, x3, x4)	      \
	x4  = x1; x1 |= x0;	      \
	x2 ^= x1; x3 = ~x3; x4 ^= x0; \
	x0 ^= x2; x1 &= x4; x4 |= x3; \
	x4 ^= x0; x0 &= x3; x1 ^= x3; \
	x3 ^= x2; x0 ^= x1; x2 &= x4; \
	x1 ^= x2; x2 &= x0;	      \
	x3 ^= x2;

#define S6(x0, x1, x2, x3, x4)	      \
			    x4  = x1; \
	x3 ^= x0; x1 ^= x2; x2 ^= x0; \
	x0 &= x3; x1 |= x3; x4 = ~x4; \
	x0 ^= x1; x1 ^= x2;	      \
	x3 ^= x4; x4 ^= x0; x2 &= x0; \
	x4 ^= x1; x2 ^= x3; x3 &= x1; \
	x3 ^= x0; x1 ^= x2;

#define S7(x0, x1, x2, x3, x4)	      \
			    x1 = ~x1; \
	x4  = x1; x0 = ~x0; x1 &= x2; \
	x1 ^= x3; x3 |= x4; x4 ^= x2; \
	x2 ^= x3; x3 ^= x0; x0 |= x1; \
	x2 &= x0; x0 ^= x4; x4 ^= x3; \
	x3 &= x0; x4 ^= x1;	      \
	x2 ^= x4; x3 ^= x1; x4 |= x0; \
	x4 ^= x1;

#define SI0(x0, x1, x2, x3, x4)	      \
		  x4  = x3; x1 ^= x0; \
	x3 |= x1; x4 ^= x1; x0 = ~x0; \
	x2 ^= x3; x3 ^= x0; x0 &= x1; \
	x0 ^= x2; x2 &= x3; x3 ^= x4; \
	x2 ^= x3; x1 ^= x3; x3 &= x0; \
	x1 ^= x0; x0 ^= x2; x4 ^= x3;

#define SI1(x0, x1, x2, x3, x4)	      \
	x1 ^= x3; x4  = x0;	      \
	x0 ^= x2; x2 = ~x2; x4 |= x1; \
	x4 ^= x3; x3 &= x1; x1 ^= x2; \
	x2 &= x4; x4 ^= x1; x1 |= x3; \
	x3 ^= x0; x2 ^= x0; x0 |= x4; \
	x2 ^= x4; x1 ^= x0;	      \
	x4 ^= x1;

#define SI2(x0, x1, x2, x3, x4)	      \
	x2 ^= x1; x4  = x3; x3 = ~x3; \
	x3 |= x2; x2 ^= x4; x4 ^= x0; \
	x3 ^= x1; x1 |= x2; x2 ^= x0; \
	x1 ^= x4; x4 |= x3; x2 ^= x3; \
	x4 ^= x2; x2 &= x1;	      \
	x2 ^= x3; x3 ^= x4; x4 ^= x0;

#define SI3(x0, x1, x2, x3, x4)	      \
			    x2 ^= x1; \
	x4  = x1; x1 &= x2;	      \
	x1 ^= x0; x0 |= x4; x4 ^= x3; \
	x0 ^= x3; x3 |= x1; x1 ^= x2; \
	x1 ^= x3; x0 ^= x2; x2 ^= x3; \
	x3 &= x1; x1 ^= x0; x0 &= x2; \
	x4 ^= x3; x3 ^= x0; x0 ^= x1;

#define SI4(x0, x1, x2, x3, x4)	      \
	x2 ^= x3; x4  = x0; x0 &= x1; \
	x0 ^= x2; x2 |= x3; x4 = ~x4; \
	x1 ^= x0; x0 ^= x2; x2 &= x4; \
	x2 ^= x0; x0 |= x4;	      \
	x0 ^= x3; x3 &= x2;	      \
	x4 ^= x3; x3 ^= x1; x1 &= x0; \
	x4 ^= x1; x0 ^= x3;

#define SI5(x0, x1, x2, x3, x4)	      \
		  x4  = x1; x1 |= x2; \
	x2 ^= x4; x1 ^= x3; x3 &= x4; \
	x2 ^= x3; x3 |= x0; x0 = ~x0; \
	x3 ^= x2; x2 |= x0; x4 ^= x1; \
	x2 ^= x4; x4 &= x0; x0 ^= x1; \
	x1 ^= x3; x0 &= x2; x2 ^= x3; \
	x0 ^= x2; x2 ^= x4; x4 ^= x3;

#define SI6(x0, x1, x2, x3, x4)	      \
		  x0 ^= x2;	      \
	x4  = x0; x0 &= x3; x2 ^= x3; \
	x0 ^= x2; x3 ^= x1; x2 |= x4; \
	x2 ^= x3; x3 &= x0; x0 = ~x0; \
	x3 ^= x1; x1 &= x2; x4 ^= x0; \
	x3 ^= x4; x4 ^= x2; x0 ^= x1; \
	x2 ^= x0;

#define SI7(x0, x1, x2, x3, x4)	      \
	x4  = x3; x3 &= x0; x0 ^= x2; \
	x2 |= x4; x4 ^= x1; x0 = ~x0; \
	x1 |= x3; x4 ^= x0; x0 &= x2; \
	x0 ^= x1; x1 &= x2; x3 ^= x2; \
	x4 ^= x3; x2 &= x3; x3 |= x0; \
	x1 ^= x4; x3 ^= x4; x4 &= x0; \
	x4 ^= x2;


CIPHER_SERPENT_API
void serpent_set_key(Serpent *object, const zuint8 *key, zusize key_size)
	{
	zuint32 *k = object->k;
	zuint32 r0, r1, r2, r3, r4;
	zusize i;

	/*---------------------------------.
	| Copy key, add padding if needed. |
	'---------------------------------*/
	for   (i = 0; i < key_size; i++)     ((zuint8 *)k)[i  ] = key[i];
	if    (i < SERPENT_KEY_MAXIMUM_SIZE) ((zuint8 *)k)[i++] = 1;
	while (i < SERPENT_KEY_MAXIMUM_SIZE) ((zuint8 *)k)[i++] = 0;

	/*-----------------------------.
	| Expand key using polynomial. |
	'-----------------------------*/
	r0 = z_uint32_little_endian(k[3]);
	r1 = z_uint32_little_endian(k[4]);
	r2 = z_uint32_little_endian(k[5]);
	r3 = z_uint32_little_endian(k[6]);
	r4 = z_uint32_little_endian(k[7]);

	KI(z_uint32_little_endian(k[0]), r0, r4, r2, 0, 0);
	KI(z_uint32_little_endian(k[1]), r1, r0, r3, 1, 1);
	KI(z_uint32_little_endian(k[2]), r2, r1, r4, 2, 2);
	KI(z_uint32_little_endian(k[3]), r3, r2, r0, 3, 3);
	KI(z_uint32_little_endian(k[4]), r4, r3, r1, 4, 4);
	KI(z_uint32_little_endian(k[5]), r0, r4, r2, 5, 5);
	KI(z_uint32_little_endian(k[6]), r1, r0, r3, 6, 6);
	KI(z_uint32_little_endian(k[7]), r2, r1, r4, 7, 7);

	KI(k[ 0], r3, r2, r0,  8,  8);
	KI(k[ 1], r4, r3, r1,  9,  9);
	KI(k[ 2], r0, r4, r2, 10, 10);
	KI(k[ 3], r1, r0, r3, 11, 11);
	KI(k[ 4], r2, r1, r4, 12, 12);
	KI(k[ 5], r3, r2, r0, 13, 13);
	KI(k[ 6], r4, r3, r1, 14, 14);
	KI(k[ 7], r0, r4, r2, 15, 15);
	KI(k[ 8], r1, r0, r3, 16, 16);
	KI(k[ 9], r2, r1, r4, 17, 17);
	KI(k[10], r3, r2, r0, 18, 18);
	KI(k[11], r4, r3, r1, 19, 19);
	KI(k[12], r0, r4, r2, 20, 20);
	KI(k[13], r1, r0, r3, 21, 21);
	KI(k[14], r2, r1, r4, 22, 22);
	KI(k[15], r3, r2, r0, 23, 23);
	KI(k[16], r4, r3, r1, 24, 24);
	KI(k[17], r0, r4, r2, 25, 25);
	KI(k[18], r1, r0, r3, 26, 26);
	KI(k[19], r2, r1, r4, 27, 27);
	KI(k[20], r3, r2, r0, 28, 28);
	KI(k[21], r4, r3, r1, 29, 29);
	KI(k[22], r0, r4, r2, 30, 30);
	KI(k[23], r1, r0, r3, 31, 31);

	k += 50;

	KI(k[-26], r2, r1, r4, 32, -18);
	KI(k[-25], r3, r2, r0, 33, -17);
	KI(k[-24], r4, r3, r1, 34, -16);
	KI(k[-23], r0, r4, r2, 35, -15);
	KI(k[-22], r1, r0, r3, 36, -14);
	KI(k[-21], r2, r1, r4, 37, -13);
	KI(k[-20], r3, r2, r0, 38, -12);
	KI(k[-19], r4, r3, r1, 39, -11);
	KI(k[-18], r0, r4, r2, 40, -10);
	KI(k[-17], r1, r0, r3, 41,  -9);
	KI(k[-16], r2, r1, r4, 42,  -8);
	KI(k[-15], r3, r2, r0, 43,  -7);
	KI(k[-14], r4, r3, r1, 44,  -6);
	KI(k[-13], r0, r4, r2, 45,  -5);
	KI(k[-12], r1, r0, r3, 46,  -4);
	KI(k[-11], r2, r1, r4, 47,  -3);
	KI(k[-10], r3, r2, r0, 48,  -2);
	KI(k[ -9], r4, r3, r1, 49,  -1);
	KI(k[ -8], r0, r4, r2, 50,   0);
	KI(k[ -7], r1, r0, r3, 51,   1);
	KI(k[ -6], r2, r1, r4, 52,   2);
	KI(k[ -5], r3, r2, r0, 53,   3);
	KI(k[ -4], r4, r3, r1, 54,   4);
	KI(k[ -3], r0, r4, r2, 55,   5);
	KI(k[ -2], r1, r0, r3, 56,   6);
	KI(k[ -1], r2, r1, r4, 57,   7);
	KI(k[  0], r3, r2, r0, 58,   8);
	KI(k[  1], r4, r3, r1, 59,   9);
	KI(k[  2], r0, r4, r2, 60,  10);
	KI(k[  3], r1, r0, r3, 61,  11);
	KI(k[  4], r2, r1, r4, 62,  12);
	KI(k[  5], r3, r2, r0, 63,  13);
	KI(k[  6], r4, r3, r1, 64,  14);
	KI(k[  7], r0, r4, r2, 65,  15);
	KI(k[  8], r1, r0, r3, 66,  16);
	KI(k[  9], r2, r1, r4, 67,  17);
	KI(k[ 10], r3, r2, r0, 68,  18);
	KI(k[ 11], r4, r3, r1, 69,  19);
	KI(k[ 12], r0, r4, r2, 70,  20);
	KI(k[ 13], r1, r0, r3, 71,  21);
	KI(k[ 14], r2, r1, r4, 72,  22);
	KI(k[ 15], r3, r2, r0, 73,  23);
	KI(k[ 16], r4, r3, r1, 74,  24);
	KI(k[ 17], r0, r4, r2, 75,  25);
	KI(k[ 18], r1, r0, r3, 76,  26);
	KI(k[ 19], r2, r1, r4, 77,  27);
	KI(k[ 20], r3, r2, r0, 78,  28);
	KI(k[ 21], r4, r3, r1, 79,  29);
	KI(k[ 22], r0, r4, r2, 80,  30);
	KI(k[ 23], r1, r0, r3, 81,  31);

	k += 50;

	KI(k[-26], r2, r1, r4,	82, -18);
	KI(k[-25], r3, r2, r0,	83, -17);
	KI(k[-24], r4, r3, r1,	84, -16);
	KI(k[-23], r0, r4, r2,	85, -15);
	KI(k[-22], r1, r0, r3,	86, -14);
	KI(k[-21], r2, r1, r4,	87, -13);
	KI(k[-20], r3, r2, r0,	88, -12);
	KI(k[-19], r4, r3, r1,	89, -11);
	KI(k[-18], r0, r4, r2,	90, -10);
	KI(k[-17], r1, r0, r3,	91,  -9);
	KI(k[-16], r2, r1, r4,	92,  -8);
	KI(k[-15], r3, r2, r0,	93,  -7);
	KI(k[-14], r4, r3, r1,	94,  -6);
	KI(k[-13], r0, r4, r2,	95,  -5);
	KI(k[-12], r1, r0, r3,	96,  -4);
	KI(k[-11], r2, r1, r4,	97,  -3);
	KI(k[-10], r3, r2, r0,	98,  -2);
	KI(k[ -9], r4, r3, r1,	99,  -1);
	KI(k[ -8], r0, r4, r2, 100,   0);
	KI(k[ -7], r1, r0, r3, 101,   1);
	KI(k[ -6], r2, r1, r4, 102,   2);
	KI(k[ -5], r3, r2, r0, 103,   3);
	KI(k[ -4], r4, r3, r1, 104,   4);
	KI(k[ -3], r0, r4, r2, 105,   5);
	KI(k[ -2], r1, r0, r3, 106,   6);
	KI(k[ -1], r2, r1, r4, 107,   7);
	KI(k[  0], r3, r2, r0, 108,   8);
	KI(k[  1], r4, r3, r1, 109,   9);
	KI(k[  2], r0, r4, r2, 110,  10);
	KI(k[  3], r1, r0, r3, 111,  11);
	KI(k[  4], r2, r1, r4, 112,  12);
	KI(k[  5], r3, r2, r0, 113,  13);
	KI(k[  6], r4, r3, r1, 114,  14);
	KI(k[  7], r0, r4, r2, 115,  15);
	KI(k[  8], r1, r0, r3, 116,  16);
	KI(k[  9], r2, r1, r4, 117,  17);
	KI(k[ 10], r3, r2, r0, 118,  18);
	KI(k[ 11], r4, r3, r1, 119,  19);
	KI(k[ 12], r0, r4, r2, 120,  20);
	KI(k[ 13], r1, r0, r3, 121,  21);
	KI(k[ 14], r2, r1, r4, 122,  22);
	KI(k[ 15], r3, r2, r0, 123,  23);
	KI(k[ 16], r4, r3, r1, 124,  24);
	KI(k[ 17], r0, r4, r2, 125,  25);
	KI(k[ 18], r1, r0, r3, 126,  26);
	KI(k[ 19], r2, r1, r4, 127,  27);
	KI(k[ 20], r3, r2, r0, 128,  28);
	KI(k[ 21], r4, r3, r1, 129,  29);
	KI(k[ 22], r0, r4, r2, 130,  30);
	KI(k[ 23], r1, r0, r3, 131,  31);

	/*---------------.
	| Apply S-boxes. |
	'---------------*/
	S3(r3, r4, r0, r1, r2); WRITE_AND_READ_KEYS(r1, r2, r4, r3,  28,  24);
	S4(r1, r2, r4, r3, r0); WRITE_AND_READ_KEYS(r2, r4, r3, r0,  24,  20);
	S5(r2, r4, r3, r0, r1); WRITE_AND_READ_KEYS(r1, r2, r4, r0,  20,  16);
	S6(r1, r2, r4, r0, r3); WRITE_AND_READ_KEYS(r4, r3, r2, r0,  16,  12);
	S7(r4, r3, r2, r0, r1); WRITE_AND_READ_KEYS(r1, r2, r0, r4,  12,   8);
	S0(r1, r2, r0, r4, r3); WRITE_AND_READ_KEYS(r0, r2, r4, r1,   8,   4);
	S1(r0, r2, r4, r1, r3); WRITE_AND_READ_KEYS(r3, r4, r1, r0,   4,   0);
	S2(r3, r4, r1, r0, r2); WRITE_AND_READ_KEYS(r2, r4, r3, r0,   0,  -4);
	S3(r2, r4, r3, r0, r1); WRITE_AND_READ_KEYS(r0, r1, r4, r2,  -4,  -8);
	S4(r0, r1, r4, r2, r3); WRITE_AND_READ_KEYS(r1, r4, r2, r3,  -8, -12);
	S5(r1, r4, r2, r3, r0); WRITE_AND_READ_KEYS(r0, r1, r4, r3, -12, -16);
	S6(r0, r1, r4, r3, r2); WRITE_AND_READ_KEYS(r4, r2, r1, r3, -16, -20);
	S7(r4, r2, r1, r3, r0); WRITE_AND_READ_KEYS(r0, r1, r3, r4, -20, -24);
	S0(r0, r1, r3, r4, r2); WRITE_AND_READ_KEYS(r3, r1, r4, r0, -24, -28);

	k -= 50;

	S1(r3, r1, r4, r0, r2); WRITE_AND_READ_KEYS(r2, r4, r0, r3,  22,  18);
	S2(r2, r4, r0, r3, r1); WRITE_AND_READ_KEYS(r1, r4, r2, r3,  18,  14);
	S3(r1, r4, r2, r3, r0); WRITE_AND_READ_KEYS(r3, r0, r4, r1,  14,  10);
	S4(r3, r0, r4, r1, r2); WRITE_AND_READ_KEYS(r0, r4, r1, r2,  10,   6);
	S5(r0, r4, r1, r2, r3); WRITE_AND_READ_KEYS(r3, r0, r4, r2,   6,   2);
	S6(r3, r0, r4, r2, r1); WRITE_AND_READ_KEYS(r4, r1, r0, r2,   2,  -2);
	S7(r4, r1, r0, r2, r3); WRITE_AND_READ_KEYS(r3, r0, r2, r4,  -2,  -6);
	S0(r3, r0, r2, r4, r1); WRITE_AND_READ_KEYS(r2, r0, r4, r3,  -6, -10);
	S1(r2, r0, r4, r3, r1); WRITE_AND_READ_KEYS(r1, r4, r3, r2, -10, -14);
	S2(r1, r4, r3, r2, r0); WRITE_AND_READ_KEYS(r0, r4, r1, r2, -14, -18);
	S3(r0, r4, r1, r2, r3); WRITE_AND_READ_KEYS(r2, r3, r4, r0, -18, -22);

	k -= 50;

	S4(r2, r3, r4, r0, r1); WRITE_AND_READ_KEYS(r3, r4, r0, r1, 28, 24);
	S5(r3, r4, r0, r1, r2); WRITE_AND_READ_KEYS(r2, r3, r4, r1, 24, 20);
	S6(r2, r3, r4, r1, r0); WRITE_AND_READ_KEYS(r4, r0, r3, r1, 20, 16);
	S7(r4, r0, r3, r1, r2); WRITE_AND_READ_KEYS(r2, r3, r1, r4, 16, 12);
	S0(r2, r3, r1, r4, r0); WRITE_AND_READ_KEYS(r1, r3, r4, r2, 12,  8);
	S1(r1, r3, r4, r2, r0); WRITE_AND_READ_KEYS(r0, r4, r2, r1,  8,  4);
	S2(r0, r4, r2, r1, r3); WRITE_AND_READ_KEYS(r3, r4, r0, r1,  4,  0);
	S3(r3, r4, r0, r1, r2); WRITE_KEYS(r1, r2, r4, r3, 0);
	}


CIPHER_SERPENT_API
void serpent_encipher(Serpent *object, zuint32 const *block, zusize block_size, zuint32 *output)
	{
	zuint32 const *k = object->k;
	zuint32	r0, r1, r2, r3, r4;

	for (block_size >>= 4; block_size; block_size--, block += 4, output += 4)
		{
		r0 = z_uint32_little_endian(block[0]);
		r1 = z_uint32_little_endian(block[1]);
		r2 = z_uint32_little_endian(block[2]);
		r3 = z_uint32_little_endian(block[3]);

					K (r0, r1, r2, r3,	0);
		S0(r0, r1, r2, r3, r4);	LK(r2, r1, r3, r0, r4,	1);
		S1(r2, r1, r3, r0, r4);	LK(r4, r3, r0, r2, r1,	2);
		S2(r4, r3, r0, r2, r1);	LK(r1, r3, r4, r2, r0,	3);
		S3(r1, r3, r4, r2, r0);	LK(r2, r0, r3, r1, r4,	4);
		S4(r2, r0, r3, r1, r4);	LK(r0, r3, r1, r4, r2,	5);
		S5(r0, r3, r1, r4, r2);	LK(r2, r0, r3, r4, r1,	6);
		S6(r2, r0, r3, r4, r1);	LK(r3, r1, r0, r4, r2,	7);
		S7(r3, r1, r0, r4, r2);	LK(r2, r0, r4, r3, r1,	8);
		S0(r2, r0, r4, r3, r1);	LK(r4, r0, r3, r2, r1,	9);
		S1(r4, r0, r3, r2, r1);	LK(r1, r3, r2, r4, r0, 10);
		S2(r1, r3, r2, r4, r0);	LK(r0, r3, r1, r4, r2, 11);
		S3(r0, r3, r1, r4, r2);	LK(r4, r2, r3, r0, r1, 12);
		S4(r4, r2, r3, r0, r1);	LK(r2, r3, r0, r1, r4, 13);
		S5(r2, r3, r0, r1, r4);	LK(r4, r2, r3, r1, r0, 14);
		S6(r4, r2, r3, r1, r0);	LK(r3, r0, r2, r1, r4, 15);
		S7(r3, r0, r2, r1, r4);	LK(r4, r2, r1, r3, r0, 16);
		S0(r4, r2, r1, r3, r0);	LK(r1, r2, r3, r4, r0, 17);
		S1(r1, r2, r3, r4, r0);	LK(r0, r3, r4, r1, r2, 18);
		S2(r0, r3, r4, r1, r2);	LK(r2, r3, r0, r1, r4, 19);
		S3(r2, r3, r0, r1, r4);	LK(r1, r4, r3, r2, r0, 20);
		S4(r1, r4, r3, r2, r0);	LK(r4, r3, r2, r0, r1, 21);
		S5(r4, r3, r2, r0, r1);	LK(r1, r4, r3, r0, r2, 22);
		S6(r1, r4, r3, r0, r2);	LK(r3, r2, r4, r0, r1, 23);
		S7(r3, r2, r4, r0, r1);	LK(r1, r4, r0, r3, r2, 24);
		S0(r1, r4, r0, r3, r2);	LK(r0, r4, r3, r1, r2, 25);
		S1(r0, r4, r3, r1, r2);	LK(r2, r3, r1, r0, r4, 26);
		S2(r2, r3, r1, r0, r4);	LK(r4, r3, r2, r0, r1, 27);
		S3(r4, r3, r2, r0, r1);	LK(r0, r1, r3, r4, r2, 28);
		S4(r0, r1, r3, r4, r2);	LK(r1, r3, r4, r2, r0, 29);
		S5(r1, r3, r4, r2, r0);	LK(r0, r1, r3, r2, r4, 30);
		S6(r0, r1, r3, r2, r4);	LK(r3, r4, r1, r2, r0, 31);
		S7(r3, r4, r1, r2, r0);	K (r0, r1, r2, r3,     32);

		output[0] = z_uint32_little_endian(r0);
		output[1] = z_uint32_little_endian(r1);
		output[2] = z_uint32_little_endian(r2);
		output[3] = z_uint32_little_endian(r3);
		}
	}


CIPHER_SERPENT_API
void serpent_decipher(Serpent *object, zuint32 const *block, zusize block_size, zuint32 *output)
	{
	const zuint32 *k = object->k;
	zuint32	r0, r1, r2, r3, r4;

	for (block_size >>= 4; block_size; block_size--, block += 4, output += 4)
		{
		r0 = z_uint32_little_endian(block[0]);
		r1 = z_uint32_little_endian(block[1]);
		r2 = z_uint32_little_endian(block[2]);
		r3 = z_uint32_little_endian(block[3]);

					 K (r0, r1, r2, r3,	32);
		SI7(r0, r1, r2, r3, r4); KL(r1, r3, r0, r4, r2, 31);
		SI6(r1, r3, r0, r4, r2); KL(r0, r2, r4, r1, r3, 30);
		SI5(r0, r2, r4, r1, r3); KL(r2, r3, r0, r4, r1, 29);
		SI4(r2, r3, r0, r4, r1); KL(r2, r0, r1, r4, r3, 28);
		SI3(r2, r0, r1, r4, r3); KL(r1, r2, r3, r4, r0, 27);
		SI2(r1, r2, r3, r4, r0); KL(r2, r0, r4, r3, r1, 26);
		SI1(r2, r0, r4, r3, r1); KL(r1, r0, r4, r3, r2, 25);
		SI0(r1, r0, r4, r3, r2); KL(r4, r2, r0, r1, r3, 24);
		SI7(r4, r2, r0, r1, r3); KL(r2, r1, r4, r3, r0, 23);
		SI6(r2, r1, r4, r3, r0); KL(r4, r0, r3, r2, r1, 22);
		SI5(r4, r0, r3, r2, r1); KL(r0, r1, r4, r3, r2, 21);
		SI4(r0, r1, r4, r3, r2); KL(r0, r4, r2, r3, r1, 20);
		SI3(r0, r4, r2, r3, r1); KL(r2, r0, r1, r3, r4, 19);
		SI2(r2, r0, r1, r3, r4); KL(r0, r4, r3, r1, r2, 18);
		SI1(r0, r4, r3, r1, r2); KL(r2, r4, r3, r1, r0, 17);
		SI0(r2, r4, r3, r1, r0); KL(r3, r0, r4, r2, r1, 16);
		SI7(r3, r0, r4, r2, r1); KL(r0, r2, r3, r1, r4, 15);
		SI6(r0, r2, r3, r1, r4); KL(r3, r4, r1, r0, r2, 14);
		SI5(r3, r4, r1, r0, r2); KL(r4, r2, r3, r1, r0, 13);
		SI4(r4, r2, r3, r1, r0); KL(r4, r3, r0, r1, r2, 12);
		SI3(r4, r3, r0, r1, r2); KL(r0, r4, r2, r1, r3, 11);
		SI2(r0, r4, r2, r1, r3); KL(r4, r3, r1, r2, r0, 10);
		SI1(r4, r3, r1, r2, r0); KL(r0, r3, r1, r2, r4,	 9);
		SI0(r0, r3, r1, r2, r4); KL(r1, r4, r3, r0, r2,	 8);
		SI7(r1, r4, r3, r0, r2); KL(r4, r0, r1, r2, r3,	 7);
		SI6(r4, r0, r1, r2, r3); KL(r1, r3, r2, r4, r0,	 6);
		SI5(r1, r3, r2, r4, r0); KL(r3, r0, r1, r2, r4,	 5);
		SI4(r3, r0, r1, r2, r4); KL(r3, r1, r4, r2, r0,	 4);
		SI3(r3, r1, r4, r2, r0); KL(r4, r3, r0, r2, r1,	 3);
		SI2(r4, r3, r0, r2, r1); KL(r3, r1, r2, r0, r4,	 2);
		SI1(r3, r1, r2, r0, r4); KL(r4, r1, r2, r0, r3,	 1);
		SI0(r4, r1, r2, r0, r3); K (r2, r3, r1, r4,	 0);

		output[0] = z_uint32_little_endian(r2);
		output[1] = z_uint32_little_endian(r3);
		output[2] = z_uint32_little_endian(r1);
		output[3] = z_uint32_little_endian(r4);
		}
	}


#if defined(CIPHER_SERPENT_BUILD_ABI) || defined(CIPHER_SERPENT_BUILD_MODULE_ABI)

	CIPHER_SERPENT_ABI ZCipherABI const abi_cipher_serpent = {
		/* test_key		 */ NULL,
		/* set_key		 */ (ZCipherSetKey )serpent_set_key,
		/* encipher		 */ (ZCipherProcess)serpent_encipher,
		/* decipher		 */ (ZCipherProcess)serpent_decipher,
		/* enciphering_size	 */ NULL,
		/* deciphering_size	 */ NULL,
		/* instance_size	 */ sizeof(Serpent),
		/* key_minimum_size	 */ SERPENT_KEY_MINIMUM_SIZE,
		/* key_maximum_size	 */ SERPENT_KEY_MAXIMUM_SIZE,
		/* key_word_size	 */ SERPENT_KEY_WORD_SIZE,
		/* enciphering_word_size */ SERPENT_WORD_SIZE,
		/* deciphering_word_size */ SERPENT_WORD_SIZE,
		/* features		 */ FALSE
	};

#endif

#if defined(CIPHER_SERPENT_BUILD_MODULE_ABI)

#	include <Z/ABIs/generic/module.h>

	static ZModuleUnit const unit = {
		"Serpent", "Serpent", Z_VERSION(1, 0, 0), &abi_cipher_serpent
	};

	static ZModuleDomain const domain = {"Cipher", Z_VERSION(1, 0, 0), 1, &unit};
	Z_API_WEAK_EXPORT ZModuleABI const __module_abi__ = {1, &domain};

#endif


/* Serpent.c EOF */
